/**
 * @file connectionCmds.c
 * @author MTF-1
 *
 * @date 22-01-2011 DD-MM-YYYY
 * @version 0.8
 *
 * @section DESCRIPTION
 *
 * Module for connection commands.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include "connectionCmds.h"

#define ERROR 1
#define NOERROR 0
#define RCVBUFSIZE 6
#define SRVNAMESIZE 100

/*Global vars*/
int iSocketFD = 0;
char sServerName[SRVNAMESIZE];

/**
 * Connect to the specified server.
 *
 * @param ptr_sIPAdd IP4 address of the server you want to connect to.
 * @param ptr_sPortNum port number of the server you want to connect to. If the port number equals zero ("0") then the default port (TCP 5002) will be used.
 * @return 1 on error and 0 on no error.
 * @author Rik Janssen
 */
int cmdConnect(char *ptr_sIPAdd, char *ptr_sPortNum)
{
    unsigned short int iPortNum;
    struct sockaddr_in sa; /*server address*/

    if(!chkConnection())
    {
        fprintf(stderr, "Error: there's already an active connection.\n");
        return ERROR;
    }

    /*Check if the port number has been specified*/
    if(!ptr_sPortNum)
    {
        iPortNum = 5002; /*set default port*/
    }
    else
    {
        iPortNum = atoi(ptr_sPortNum); /*Convert string to int*/
    }
    
    /*Create socket*/
    if((iSocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
    {
        perror("Error: can't make connection; failed to create socket.\n");
        return ERROR;
    }
    
    /*Fill the server address structure*/
    memset(&sa, 0, sizeof(sa)); /*Set structure to zero*/
    sa.sin_family = AF_INET;
    sa.sin_port = htons(iPortNum);
    inet_aton(ptr_sIPAdd, &(sa.sin_addr));

    /*Try to connect to server*/
    if(connect(iSocketFD, (struct sockaddr *)&sa, sizeof(sa)) == -1)
    {
        perror("Error: can't make connection; failed to connect.\n");

        if(close(iSocketFD))
        {
            perror("Critical error: failed to connect to server and can't close socket.\n");
            exit(-1);
        }

        iSocketFD = 0;

        return ERROR;
    }
    
    /*Get server name (only the name - not the FQDN)*/
    if(getnameinfo((struct sockaddr *)&sa, sizeof sa, sServerName, SRVNAMESIZE, NULL, 0, NI_NOFQDN))
    {
        strcpy(sServerName, "unknown");
    }
    
    printf("Connected to %s.\n", sServerName);
    return NOERROR;
}

/**
 * Disconnect the current server connection.
 *
 * @return 1 on error and 0 on no error.
 * @author Rik Janssen
 */
int cmdDisconnect()
{
    if(chkConnection())
    {
        fprintf(stderr, "Error: there's currently no active connection.\n");
        return ERROR;
    }

    printf("Disconnecting from %s...\n", sServerName);

    if(send(iSocketFD, "QUIT", 5, 0) == -1)
    {
        perror("Critical error: failed to propely disconnect from server; sending \"QUIT\" to server failed.\n");
        exit(-1);
    }

    if(close(iSocketFD))
    {
        perror("Critical error: failed to propely disconnect from server; can't close socket.\n");
        exit(-1);
    }

    iSocketFD = 0;

    return NOERROR;
}

/**
 * Check if there's an active connection with a server AND if there's an active socket present.
 *
 * @return 1 on error (there's NO connection) 0 on no error (there's a connection).
 * @author Rik Janssen
 */
int chkConnection()
{
    int iError = ERROR; /*There's no active socket*/
    int iBytesRcvd = 0;
    char sRcvBuffer[RCVBUFSIZE];

    if(iSocketFD)
    {
        iError = NOERROR; /*There's an active socket*/

        if(send(iSocketFD, "PING", 5, 0) == -1)
        {
            iError = ERROR; /*Sending failed*/
        }
        else
        {
            if((iBytesRcvd = recv(iSocketFD, sRcvBuffer, RCVBUFSIZE-1, 0)) <= 0)
            {
                iError =  ERROR; /*Error or peer shutdown*/
            }
            else
            {
                sRcvBuffer[iBytesRcvd] = '\0'; /*Terminate the received message*/
                if(strcasecmp(sRcvBuffer, "PONG"))
                {
                    iError = ERROR; /*There's no active connection (sRcvBuffer != PONG)*/
                }
            }
        }

        /*Check if there's an connection error (did send, recv or strcasecmp return an error?)*/
        if(iError)
        {
            if(close(iSocketFD))
            {
                perror("Critical error: no active connection detected and failed to close socket.\n");
                exit(-1);
            }
            else
            {
                iSocketFD = 0;
            }
        }
    }

    return iError;
}
